package cn.springcloud.fix.common.core.generator;

import cn.springcloud.fix.common.core.utils.StringUtil;
import com.google.common.collect.Lists;
import org.springframework.util.PropertyPlaceholderHelper;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CodeGenerator {

    public static void main(String[] args) throws IOException {
        String basePackageName = "cn.springcloud.fix.server.";

        Map<String, String> properties = new HashMap<>();
        properties.put("controlPackage", basePackageName + "controller");
        properties.put("servicePackage", basePackageName + "service");
        properties.put("serviceImplPackage", basePackageName + "service");

        String scope = "Message";
        String serverPath = "/server/message";
        String outputPath = "D:\\test\\";
        String serverName = "springcloud-server";
        List<String> methods = Lists.newArrayList("addMessage", "delMessage", "messageList");
        boolean useFeignFallback = true;

        String servicePackage = basePackageName + "service";
        String serviceImplPackage = basePackageName + "service";
        String requestPackage = basePackageName + "api." + scope.toLowerCase() + ".request";
        String responsePackage = basePackageName + "api." + scope.toLowerCase() + ".response";
        String feignServicePackage = basePackageName + "api." + scope.toLowerCase() + ".feign";
        String feignFallbackPackage = feignServicePackage + ".fallback";
        String feignFallbackFactoryPackage = feignServicePackage + ".factory";

        String controllerName = scope + "Controller";
        String serviceName = "I" + scope + "Service";
        String serviceImplName = serviceName + "Impl";
        String feignServiceName = scope + "Service";
        String feignFallbackName = scope + "ServiceFallBack";
        String feignFallbackFactoryName = scope + "ServiceFallbackFactory";

        properties.put("serviceName", serviceName);
        properties.put("serviceImplName", serviceImplName);

        String controllerPath = outputPath + properties.get("controlPackage").replaceAll("\\.", "/") + "/" + controllerName + ".java";
        String servicePath = outputPath + servicePackage.replaceAll("\\.", "/") + "/" + serviceName + ".java";
        String serviceImplPath = outputPath + serviceImplPackage.replaceAll("\\.", "/") + "/" + serviceImplName + ".java";
        String feignPath = outputPath + feignServicePackage.replaceAll("\\.", "/") + "/" + feignServiceName + ".java";
        String feignFallbackPath = outputPath + feignFallbackPackage.replaceAll("\\.", "/") + "/" + feignFallbackName + ".java";
        String feignFallbackFactoryPath = outputPath + feignFallbackFactoryPackage.replaceAll("\\.", "/") + "/" + feignFallbackFactoryName + ".java";

        String rt = "\r\n";

        StringBuilder serviceMethodCode = new StringBuilder();
        StringBuilder serviceImplMethodCode = new StringBuilder();
        StringBuilder feignMethodCode = new StringBuilder();
        StringBuilder controllerMethodCode = new StringBuilder();

        for (String method : methods) {
            String requestName = StringUtil.upperFirst(method) + "Request";
            String responseName = StringUtil.upperFirst(method) + "Response";
            String requestPath = outputPath + requestPackage.replaceAll("\\.", "/") + "/" + requestName + ".java";
            String responsePath = outputPath + responsePackage.replaceAll("\\.", "/") + "/" + responseName + ".java";

            String requestSrcCode = "package " + requestPackage + ";" + rt + rt
                    + "import cn.springcloud.fix.common.core.base.BaseRequest;" + rt
                    + "import cn.springcloud.fix.common.core.exception.ApiRuleException;" + rt
                    + "import " + responsePackage + "." + responseName + ";" + rt + rt
                    + "public class " + requestName + " extends BaseRequest<" + responseName + "> {" + rt + rt
                    + "\t" + "@Override" + rt
                    + "\t" + "public Class getResponseClass() {" + rt
                    + "\t\t" + "return " + responseName + ".class;" + rt
                    + "\t" + "}" + rt + rt
                    + "\t" + "@Override" + rt
                    + "\t" + "public String getApiMethodName() {" + rt
                    + "\t\t" + "return null;" + rt
                    + "\t" + "}" + rt + rt
                    + "\t" + "@Override" + rt
                    + "\t" + "public void check() throws ApiRuleException {" + rt + rt
                    + "\t" + "}" + rt
                    + "}";

            String responseSrcCode = "package " + responsePackage + ";" + rt + rt
                    + "import cn.springcloud.fix.common.core.base.BaseResponse;" + rt + rt
                    + "public class " + responseName + " extends BaseResponse {" + rt + rt
                    + "}";

            serviceMethodCode.append(rt).append(rt)
                    .append("\t").append(responseName).append(" ")
                    .append(method)
                    .append("(").append(requestName).append(" request, HeaderInfo headerInfo);");

            serviceImplMethodCode.append(rt).append(rt)
                    .append("\t").append("@Override").append(rt)
                    .append("\t").append("public ").append(responseName).append(" ").append(method)
                    .append("(").append(requestName).append(" request, HeaderInfo headerInfo) {").append(rt)
                    .append("\t\t").append("return null;").append(rt)
                    .append("\t}");

            feignMethodCode.append(rt).append(rt)
                    .append("\t").append("@PostMapping(\"/").append(method).append("\")").append(rt)
                    .append("\t").append(responseName).append(" ")
                    .append(method)
                    .append("(@RequestBody ").append(requestName).append(" request, HeaderInfo headerInfo);");

            controllerMethodCode.append(rt).append(rt)
                    .append("\t").append("@PostMapping(\"/").append(method).append("\")").append(rt)
                    .append("\t").append("public ").append(responseName).append(" ")
                    .append(method)
                    .append("(@RequestBody ").append(requestName).append(" request, HeaderInfo headerInfo) {").append(rt)
                    .append("\t\t").append("return ")
                    .append(StringUtil.lowerFirst(serviceName)).append(".").append(method).append("(request, headerInfo);")
                    .append(rt)
                    .append("\t}");

            writeFile(requestPath, requestSrcCode);
            writeFile(responsePath, responseSrcCode);
        }

        String serviceSrcCode = "package " + servicePackage + ";" + rt + rt
                + "import cn.springcloud.fix.common.core.base.HeaderInfo;" + rt
                + "import " + requestPackage + ".*;" + rt
                + "import " + responsePackage + ".*;" + rt + rt
                + "public interface ${serviceName} {"
                + serviceMethodCode.toString() + rt
                + "}";

        String serviceImplSrcCode = "package " + serviceImplPackage + ";" + rt + rt
                + "import cn.springcloud.fix.common.core.base.HeaderInfo;" + rt
                + "import lombok.extern.slf4j.Slf4j;" + rt
                + "import org.springframework.stereotype.Service;" + rt
                + "import " + requestPackage + ".*;" + rt
                + "import " + responsePackage + ".*;" + rt + rt
                + "@Slf4j" + rt
                + "@Service" + rt
                + "public interface ${serviceImplName} implements ${serviceName} {"
                + serviceImplMethodCode.toString() + rt
                + "}";

        String controllerSrcCode = "package ${controlPackage};" + rt + rt
                + "import org.springframework.web.bind.annotation.PostMapping;" + rt
                + "import org.springframework.beans.factory.annotation.Autowired;" + rt
                + "import org.springframework.web.bind.annotation.RequestBody;" + rt
                + "import org.springframework.web.bind.annotation.RequestMapping;" + rt
                + "import org.springframework.web.bind.annotation.RestController;" + rt + rt
                + "@RestController" + rt
                + "@RequestMapping(" + "\"/" + scope.toLowerCase() + "\")" + rt
                + "public class " + controllerName + " {" + rt + rt
                + "\t" + "@Autowired " + rt
                + "\t" + "private " + serviceName + " " + StringUtil.lowerFirst(serviceName) + ";"
                + controllerMethodCode.toString() + rt
                + "}";

        String feignServiceCode = "package " + feignServicePackage + ";" + rt + rt
                + "import cn.springcloud.fix.common.core.base.HeaderInfo;" + rt
                + "import org.springframework.cloud.openfeign.FeignClient;" + rt
                + "import org.springframework.web.bind.annotation.PostMapping;" + rt
                + "import org.springframework.web.bind.annotation.RequestBody;" + rt
                + "import " + requestPackage + ".*;" + rt
                + "import " + responsePackage + ".*;" + rt;

        if (useFeignFallback) {
            feignServiceCode += "import " + feignFallbackPackage + "." + feignFallbackName + ";" + rt + rt
                    + "@FeignClient(name = \"" + serverName + "\", path = \"" + serverPath + "\", fallback = " + feignFallbackName + ".class)" + rt
                    + "public interface " + feignServiceName + " {"
                    + feignMethodCode.toString() + rt
                    + "}";

            String feignFallbackSrcCode = "package " + feignFallbackPackage + ";" + rt + rt
                    + "import cn.springcloud.fix.common.core.base.HeaderInfo;" + rt
                    + "import lombok.extern.slf4j.Slf4j;" + rt
                    + "import org.springframework.stereotype.Component;" + rt
                    + "import " + requestPackage + ".*;" + rt
                    + "import " + responsePackage + ".*;" + rt + rt
                    + "@Slf4j" + rt
                    + "@Component" + rt
                    + "public class " + feignFallbackName + " implements " + feignServiceName + " {"
                    + serviceImplMethodCode.toString() + rt
                    + "}";

            writeFile(feignFallbackPath, feignFallbackSrcCode);
        } else {
            feignServiceCode += "import " + feignFallbackFactoryPackage + "." + feignFallbackFactoryName + ";" + rt + rt
                    + "@FeignClient(name = \"" + serverName + "\", path = \"" + serverPath + "\", fallbackFactory = " + feignFallbackFactoryName + ".class)" + rt
                    + "public interface " + feignServiceName + " {" + rt + rt
                    + feignMethodCode.toString() + rt
                    + "}";

            String feignFallbackFactorySrcCode = "package " + feignFallbackFactoryPackage + ";" + rt + rt
                    + "import feign.hystrix.FallbackFactory;" + rt
                    + "import lombok.extern.slf4j.Slf4j;" + rt
                    + "import org.springframework.stereotype.Component;" + rt + rt
                    + "@Slf4j" + rt
                    + "@Component" + rt
                    + "public class " + feignFallbackFactoryName + " implements FallbackFactory<" + feignServiceName + "> {" + rt + rt
                    + "\t" + "@Override" + rt
                    + "\t" + "public " + feignServiceName + " create(Throwable throwable) {" + rt
                    + "\t\t" + "log.error(throwable.getMessage(), throwable);" + rt
                    + "\t\t" + "return null;" + rt
                    + "\t" + "}"
                    + "}";

            writeFile(feignFallbackFactoryPath, feignFallbackFactorySrcCode);
        }

        PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}");
        controllerSrcCode = helper.replacePlaceholders(controllerSrcCode, properties::get);
        serviceSrcCode = helper.replacePlaceholders(serviceSrcCode, properties::get);
        serviceImplSrcCode = helper.replacePlaceholders(serviceImplSrcCode, properties::get);

        writeFile(controllerPath, controllerSrcCode);
        writeFile(servicePath, serviceSrcCode);
        writeFile(serviceImplPath, serviceImplSrcCode);
        writeFile(feignPath, feignServiceCode);
    }

    private static void writeFile(String path, String srcCode) throws IOException {
        File file = new File(path);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        FileWriter fw = new FileWriter(file);
        fw.write(srcCode);
        fw.flush();
        fw.close();
    }
}
